﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nature.Numerics.Basic;

namespace Nature
{
    public partial class IdealGasMixture
    {
        void calc_elementMoleFractions(double[] elxk)
        {
            double totalSum = 0.0;
            double elSum = 0.0;
            var x = SpeciesMoleFractions.Data;
            var spel = Model.SpelMatrix;
            for (int iel = 0; iel < Nel; ++iel)
            {
                elSum = 0.0;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    elSum += x[isp] * spel[isp][iel];
                }
                elxk[iel] = elSum;
                totalSum += elSum;
            }
            for (int iel = 0; iel < Nel; ++iel) { elxk[iel] /= totalSum; }
        }

        void calc_elementMassFractions(double[] elyk)
        {
            var ex = ElementMoleFractions.Data;
            var ew = Model.ElementMolarMasses;
            double sum = 0.0;
            for (int iel = 0; iel < Nel; ++iel)
            {
                double add = ex[iel] * ew[iel];
                elyk[iel] = ex[iel] * ew[iel];
                sum += add;
            }
            for (int iel = 0; iel < Nel; ++iel) { elyk[iel] /= sum; }
        }

        void calc_speciesMoleFractionsLog(double[] xklog)
        {
            var xk = SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp) { xklog[isp] = Math.Log(1.0e-30 + xk[isp]); }
        }

        void calc_speciesMolarConcentrations(double[] ck)
        {
            double csum = MolarDensity;
            double[] x = SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                ck[isp] = x[isp] * csum;
            }
        }

        void calc_speciesMassFractions(double[] y)
        {
            if (m_speciesMoleFractions.UpToDate)
            {
                double wmix = MolarMass;
                var w = Model.SpeciesMolarMasses;
                var x = m_speciesMoleFractions.Array;
                for (int isp = 0; isp < Nsp; ++isp) { y[isp] = x[isp] * w[isp] / wmix; }
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        void calc_speciesMoleFractions(double[] x)
        {
            if (m_speciesMassFractions.UpToDate)
            {
                double wmix = MolarMass;
                var w = Model.SpeciesMolarMasses;
                var y = m_speciesMassFractions.Array;
                for (int isp = 0; isp < Nsp; ++isp) { x[isp] = wmix * y[isp] / w[isp]; }
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        double calc_molarmass()
        {
            if (m_speciesMoleFractions.UpToDate)
            {
                double[] x = m_speciesMoleFractions.Array;
                var w = Model.SpeciesMolarMasses;
                double sum = 0.0;
                for (int isp = 0; isp < Nsp; ++isp) { sum += x[isp] * w[isp]; }
                return sum;
            }
            else if (m_speciesMassFractions.UpToDate)
            {
                double[] y = m_speciesMassFractions.Array;
                var w = Model.SpeciesMolarMasses;
                double sum = 0.0;
                for (int isp = 0; isp < Nsp; ++isp) { sum += y[isp] / w[isp]; }
                return 1.0 / sum;
            }
            else if (m_speciesMolarConcentrations.UpToDate)
            {
                throw new NotImplementedException();
            }
            throw new NotImplementedException();
        }

        double calc_pressure()
        {
            if (m_molarDensity.UpToDate && m_temperature.UpToDate)
            {
                return Constants.Rgas * m_temperature.Value / m_molarDensity.Value;
            }
            else if (m_s.UpToDate || m_molarS.UpToDate || m_specificS.UpToDate)
            {
                if (m_temperature.UpToDate)
                {
                    throw new NotImplementedException();
                }
                else if (m_molarDensity.UpToDate)
                {
                    throw new NotImplementedException();
                }
            } 
            throw new InvalidOperationException();
        }

        double calc_temperature()
        {
            if (m_molarDensity.UpToDate && m_pressure.UpToDate)
            {
                return m_pressure.Value / m_molarDensity.Value / Constants.Rgas;
            }
            else if (m_h.UpToDate || m_molarH.UpToDate || m_specificH.UpToDate)
            {
                throw new NotImplementedException();
            }
            else if (m_s.UpToDate || m_molarS.UpToDate || m_specificS.UpToDate)
            {
                if (m_pressure.UpToDate)
                {
                    throw new NotImplementedException();
                }
                else if (m_molarDensity.UpToDate)
                {
                    throw new NotImplementedException();
                }
            }            
            throw new InvalidOperationException();
        }

        double calc_mixtureMolarDensity()
        {
            if (m_temperature.UpToDate && m_pressure.UpToDate)
            {
                return m_pressure.Value / m_temperature.Value / Constants.Rgas;
            }
            else if (m_s.UpToDate || m_molarS.UpToDate || m_specificS.UpToDate)
            {
                if (m_pressure.UpToDate)
                {
                    throw new NotImplementedException();
                }
                else if (m_temperature.UpToDate)
                {
                    throw new NotImplementedException();
                }
            }
            else if (m_massDensity.UpToDate)
            {
                return m_massDensity.Value / MolarMass;
            }
            throw new InvalidOperationException();
        }

        double calc_massDensity()
        {
            return MolarDensity * MolarMass;
        }

        double calc_mixtureSpeedOfSound()
        {
            double cp = Cp;
            double gamma = cp / (cp - 1.0d);
            return Math.Sqrt(gamma * Constants.Rgas * this.Temperature / this.MolarMass);
        }

        double calc_temperatureLog()
        {
            return Math.Log(this.Temperature);
        }

        double calc_pressureAtmLog()
        {
            return Math.Log(this.Pressure / Constants.Atmosphere);
        }

        double calc_mixtureMolarCp()
        {
            if (m_cp.UpToDate)
            {
                return m_cp.Value * Constants.Rgas;
            }
            else if(m_specificCp.UpToDate)
            {
                return m_specificCp.Value * this.MolarMass;
            }
            else if (m_cv.UpToDate)
            {
                return (m_cv.Value + 1d) * Constants.Rgas;
            }
            else if (m_molarCv.UpToDate)
            {
                return m_molarCv.Value + Constants.Rgas;
            }
            else if (m_specificCv.UpToDate)
            {
                return m_specificCv.Value * this.MolarMass + Constants.Rgas;
            }
            double cp = 0d;
            double[] cpk = this.SpeciesCp.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cp += cpk[isp] * xk[isp];
            }
            return cp * Constants.Rgas;
        }

        double calc_mixtureMolarCv()
        {
            if (m_cv.UpToDate)
            {
                return m_cv.Value * Constants.Rgas;
            }
            else if (m_specificCv.UpToDate)
            {
                return m_specificCv.Value * this.MolarMass;
            }
            else if (m_cp.UpToDate)
            {
                return (m_cp.Value - 1d) * Constants.Rgas; ;
            }
            else if (m_molarCp.UpToDate)
            {
                return (m_molarCp.Value - Constants.Rgas);
            }
            else if (m_specificCp.UpToDate)
            {
                return (m_specificCp.Value * this.MolarMass) - Constants.Rgas;
            }
            double cv = 0d;
            double[] cvk = this.SpeciesCv.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cv += cvk[isp] * xk[isp];
            }
            return cv * Constants.Rgas;
        }

        double calc_mixtureMolarS()
        {
            if (m_s.UpToDate)
            {
                return m_s.Value * Constants.Rgas;
            }
            else if (m_specificS.UpToDate)
            {
                return m_specificS.Value * this.MolarMass;
            }
            double s = 0d;
            double[] sk = this.SpeciesS.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] logxk = this.SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                s += xk[isp] * (sk[isp] - logxk[isp]);
            }
            return (s - this.PressureAtmLog) * Constants.Rgas;
        }

        double calc_mixtureMolarH()
        {
            if (m_h.UpToDate)
            {
                return m_h.Value * this.Temperature * Constants.Rgas;
            }
            else if (m_specificH.UpToDate)
            {
                return m_specificH.Value * this.MolarMass;
            }
            else if (m_u.UpToDate)
            {
                return (m_u.Value + 1d) * this.Temperature * Constants.Rgas;
            }
            else if (m_molarU.UpToDate)
            {
                return m_molarU.Value + this.Temperature * Constants.Rgas;
            }
            else if (m_specificU.UpToDate)
            {
                return m_specificU.Value * this.MolarMass + this.Temperature * Constants.Rgas;
            }
            double h = 0d;
            double[] hk = this.SpeciesH.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                h += hk[isp] * xk[isp];
            }
            return h * Constants.Rgas * this.Temperature;
        }

        double calc_mixtureMolarU()
        {
            if (m_u.UpToDate)
            {
                return m_u.Value * this.Temperature * Constants.Rgas;
            }
            else if (m_specificU.UpToDate)
            {
                return m_specificU.Value * this.MolarMass;
            }
            else if (m_h.UpToDate)
            {
                return (m_h.Value - 1d) * this.Temperature * Constants.Rgas;
            }
            else if (m_molarH.UpToDate)
            {
                return m_molarH.Value - this.Temperature * Constants.Rgas;
            }
            else if (m_specificH.UpToDate)
            {
                return m_specificH.Value * this.MolarMass - this.Temperature * Constants.Rgas;
            }
            double u = 0d;
            double[] uk = this.SpeciesU.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                u += uk[isp] * xk[isp];
            }
            return u * Constants.Rgas * this.Temperature;
        }

        double calc_mixtureMolarG()
        {
            if (m_g.UpToDate)
            {
                return m_g.Value * this.Temperature * Constants.Rgas;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificG.Value * this.MolarMass;
            }
            else if (m_f.UpToDate)
            {
                return (m_f.Value + 1d) * this.Temperature * Constants.Rgas;
            }
            else if (m_molarF.UpToDate)
            {
                return m_molarF.Value + this.Temperature * Constants.Rgas;
            }
            else if (m_specificF.UpToDate)
            {
                return m_specificF.Value * this.MolarMass + this.Temperature * Constants.Rgas;
            }
            double g = 0d;
            double[] gk = this.SpeciesG.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] xklog = this.SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                g += xk[isp] * (gk[isp] + xklog[isp]);
            }
            return (g + this.PressureAtmLog) * Constants.Rgas * this.Temperature;
        }

        double calc_mixtureMolarF()
        {
            if (m_f.UpToDate)
            {
                return m_f.Value * this.Temperature * Constants.Rgas;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificF.Value * this.MolarMass;
            }
            else if (m_g.UpToDate)
            {
                return (m_g.Value - 1d) * this.Temperature * Constants.Rgas;
            }
            else if (m_molarG.UpToDate)
            {
                return m_molarG.Value - this.Temperature * Constants.Rgas;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificG.Value * this.MolarMass - this.Temperature * Constants.Rgas;
            }
            double f = 0d;
            double[] fk = this.SpeciesF.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] xklog = this.SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                f += xk[isp] * (fk[isp] + xklog[isp]);
            }
            return (f + this.PressureAtmLog) * Constants.Rgas * this.Temperature;
        }

        double calc_mixtureSpecificCp()
        {
            if (m_cp.UpToDate)
            {
                return m_cp.Value * Constants.Rgas / this.MolarMass;
            }
            else if (m_molarCp.UpToDate)
            {
                return m_molarCp.Value / this.MolarMass;
            }
            else if (m_cv.UpToDate)
            {
                return (m_cv.Value + 1d) * Constants.Rgas / this.MolarMass;
            }
            else if (m_molarCv.UpToDate)
            {
                return (m_molarCv.Value + Constants.Rgas) / this.MolarMass;
            }
            else if (m_specificCv.UpToDate)
            {
                return m_specificCv.Value + Constants.Rgas / this.MolarMass;
            }
            double cp = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] cpk = this.SpeciesCp.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cp += xk[isp] * cpk[isp];
            }
            return cp * Constants.Rgas / this.MolarMass;
        }


        double calc_mixtureSpecificCv()
        {
            if (m_cv.UpToDate)
            {
                return m_cv.Value * Constants.Rgas / this.MolarMass;
            }
            else if (m_molarCv.UpToDate)
            {
                return m_molarCv.Value / this.MolarMass;
            }
            else if (m_cp.UpToDate)
            {
                return (m_cp.Value - 1d) * Constants.Rgas / this.MolarMass;
            }
            else if (m_molarCp.UpToDate)
            {
                return (m_molarCp.Value - Constants.Rgas) / this.MolarMass;
            }
            else if (m_specificCp.UpToDate)
            {
                return m_specificCp.Value - Constants.Rgas / this.MolarMass;
            }
            double cv = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] cvk = this.SpeciesCv.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cv += xk[isp] * cvk[isp];
            }
            return cv * Constants.Rgas / this.MolarMass;
        }

        double calc_mixtureSpecificH()
        {
            if (m_h.UpToDate)
            {
                return m_h.Value * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarH.UpToDate)
            {
                return m_molarH.Value / this.MolarMass;
            }
            else if (m_u.UpToDate)
            {
                return (m_u.Value + 1d) * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarU.UpToDate)
            {
                return (m_molarU.Value + Constants.Rgas * this.Temperature) / this.MolarMass;
            }
            else if (m_specificU.UpToDate)
            {
                return m_specificU.Value + Constants.Rgas * this.Temperature / this.MolarMass;
            }
            double h = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] hk = this.SpeciesH.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                h += xk[isp] * hk[isp];
            }
            m_h.Value = h;
            return h * Constants.Rgas * this.Temperature / this.MolarMass;
        }

        double calc_mixtureSpecificU()
        {
            if (m_u.UpToDate)
            {
                return m_u.Value * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarU.UpToDate)
            {
                return m_molarU.Value / this.MolarMass;
            }
            else if (m_h.UpToDate)
            {
                return (m_h.Value - 1d) * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarH.UpToDate)
            {
                return (m_molarH.Value - Constants.Rgas * this.Temperature) / this.MolarMass;
            }
            else if (m_specificH.UpToDate)
            {
                return m_specificH.Value - Constants.Rgas * this.Temperature / this.MolarMass;
            }
            double u = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] uk = this.SpeciesU.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                u += xk[isp] * uk[isp];
            }
            m_u.Value = u;
            return u * Constants.Rgas * this.Temperature / this.MolarMass;
        }

        double calc_mixtureSpecificG()
        {
            if (m_g.UpToDate)
            {
                return m_g.Value * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarG.UpToDate)
            {
                return m_molarG.Value / this.MolarMass;
            }
            else if (m_f.UpToDate)
            {
                return (m_f.Value + 1d) * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarF.UpToDate)
            {
                return (m_molarF.Value + Constants.Rgas * this.Temperature) / this.MolarMass;
            }
            else if (m_specificF.UpToDate)
            {
                return m_specificF.Value + Constants.Rgas * this.Temperature / this.MolarMass;
            }
            double g = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] xklog = this.SpeciesMoleFractionsLog.Data;
            double[] gk = this.SpeciesG.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                g += xk[isp] * ( gk[isp] + xklog[isp]);
            }
            g += this.PressureAtmLog;
            m_g.Value = g;
            return g * Constants.Rgas * this.Temperature / this.MolarMass;
        }

        double calc_mixtureSpecificF()
        {
            if (m_f.UpToDate)
            {
                return m_f.Value * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarF.UpToDate)
            {
                return m_molarF.Value / this.MolarMass;
            }
            else if (m_g.UpToDate)
            {
                return (m_g.Value - 1d) * Constants.Rgas * this.Temperature / this.MolarMass;
            }
            else if (m_molarG.UpToDate)
            {
                return (m_molarG.Value - Constants.Rgas * this.Temperature) / this.MolarMass;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificG.Value - Constants.Rgas * this.Temperature / this.MolarMass;
            }
            double f = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] xklog = this.SpeciesMoleFractionsLog.Data;
            double[] fk = this.SpeciesF.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                f += xk[isp] * (fk[isp] + xklog[isp]);
            }
            f += this.PressureAtmLog;
            m_f.Value = f;
            return f * Constants.Rgas * this.Temperature / this.MolarMass;
        }

        double calc_mixtureSpecificS()
        {
            if (m_s.UpToDate)
            {
                return m_s.Value * Constants.Rgas / this.MolarMass;
            }
            else if (m_molarS.UpToDate)
            {
                return m_molarS.Value / this.MolarMass;
            }           
            double s = 0d;
            double[] xk = this.SpeciesMoleFractions.Data;
            double[] xklog = this.SpeciesMoleFractionsLog.Data;
            double[] fk = this.SpeciesS.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                s += xk[isp] * (fk[isp] - xklog[isp]);
            }
            s -= this.PressureAtmLog;
            m_s.Value = s;
            return s * Constants.Rgas / this.MolarMass;
        }

        double calc_mixtureCp()
        {
            if (m_molarCp.UpToDate)
            {
                return m_molarCp.Value / Constants.Rgas;
            }
            else if (m_specificCp.UpToDate)
            {
                return m_specificCp.Value * this.MolarMass / Constants.Rgas;
            }
            else if (m_cv.UpToDate)
            {
                return m_cv.Value + 1d;
            }
            else if (m_molarCv.UpToDate)
            {
                return m_molarCv.Value / Constants.Rgas + 1d;
            }
            else if (m_specificCv.UpToDate)
            {
                return 1d + m_specificCv.Value * this.MolarMass / Constants.Rgas;
            }
            double cp = 0.0d;
            double[] cpk = this.SpeciesCp.Data;
            double[] xk = this.SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cp += xk[isp] * cpk[isp];
            }
            return cp;
        }

        double calc_mixtureCv()
        {
            if (m_molarCv.UpToDate)
            {
                return m_molarCv.Value / Constants.Rgas;
            }
            else if (m_specificCv.UpToDate)
            {
                return m_specificCv.Value * this.MolarMass / Constants.Rgas;
            }
            else if (m_cp.UpToDate)
            {
                return m_cp.Value - 1d;
            }
            else if (m_molarCp.UpToDate)
            {
                return m_molarCp.Value / Constants.Rgas - 1d;
            }
            else if (m_specificCp.UpToDate)
            {
                return 1d + m_specificCp.Value * this.MolarMass / Constants.Rgas;
            }
            
            double cv = 0.0d;
            double[] cvk = SpeciesCv.Data;
            double[] xk = SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                cv += xk[isp] * cvk[isp];
            }
            return cv;
        }

        double calc_mixtureH()
        {
            if (m_molarH.UpToDate)
            {
                return m_molarH.Value / Constants.Rgas / this.Temperature;
            }
            else if (m_specificH.UpToDate)
            {
                return m_specificH.Value * this.MolarMass / Constants.Rgas / this.Temperature;
            }
            else if (m_u.UpToDate)
            {
                return m_u.Value + 1d;
            }
            else if (m_molarU.UpToDate)
            {
                return m_molarU.Value / Constants.Rgas / this.Temperature + 1d;
            }
            else if (m_specificU.UpToDate)
            {
                return 1d + m_specificU.Value * this.MolarMass / Constants.Rgas / this.Temperature;
            }
            double h = 0.0d;
            double[] hk = SpeciesH.Data;
            double[] xk = SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                h += xk[isp] * hk[isp];
            }
            return h;
        }

        double calc_mixtureU()
        {
            if (m_molarU.UpToDate)
            {
                return m_molarU.Value / Constants.Rgas / this.Temperature;
            }
            else if (m_specificU.UpToDate)
            {
                return m_specificU.Value * this.MolarMass / Constants.Rgas / this.Temperature;
            }
            else if (m_h.UpToDate)
            {
                return m_h.Value - 1d;
            }
            else if (m_molarH.UpToDate)
            {
                return m_molarH.Value / Constants.Rgas / this.Temperature - 1d;
            }
            else if (m_specificH.UpToDate)
            {
                return m_specificH.Value * this.MolarMass / Constants.Rgas / this.Temperature - 1d;
            }
            double u = 0.0d;
            double[] uk = SpeciesU.Data;
            double[] xk = SpeciesMoleFractions.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                u += xk[isp] * uk[isp];
            }
            return u;
        }

        double calc_mixtureG()
        {
            if (m_molarG.UpToDate)
            {
                return m_molarG.Value / Constants.Rgas / this.Temperature;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificG.Value * this.MolarMass / Constants.Rgas / this.Temperature;
            }
            else if (m_f.UpToDate)
            {
                return m_f.Value + 1d;
            }
            else if (m_molarF.UpToDate)
            {
                return m_molarF.Value / Constants.Rgas / this.Temperature + 1d;
            }
            else if (m_specificF.UpToDate)
            {
                return m_specificF.Value * this.MolarMass / Constants.Rgas / this.Temperature + 1d;
            }
            double g = 0.0d;
            double[] gk = SpeciesG.Data;
            double[] xk = SpeciesMoleFractions.Data;
            double[] xlofk = SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                g += xk[isp] * (gk[isp] + xlofk[isp]);
            }
            return g + this.PressureAtmLog;
        }

        double calc_mixtureF()
        {
            if (m_molarF.UpToDate)
            {
                return m_molarF.Value / Constants.Rgas / this.Temperature;
            }
            else if (m_specificF.UpToDate)
            {
                return m_specificF.Value * this.MolarMass / Constants.Rgas / this.Temperature;
            }
            else if (m_g.UpToDate)
            {
                return m_g.Value - 1d;
            }
            else if (m_molarG.UpToDate)
            {
                return m_molarG.Value / Constants.Rgas / this.Temperature - 1d;
            }
            else if (m_specificG.UpToDate)
            {
                return m_specificG.Value * this.MolarMass / Constants.Rgas / this.Temperature - 1d;
            }
            double f = 0.0d;
            double[] fk = SpeciesF.Data;
            double[] xk = SpeciesMoleFractions.Data;
            double[] xlofk = SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                f += xk[isp] * (fk[isp] + xlofk[isp]);
            }
            return f + PressureAtmLog;
        }

        double calc_mixtureS()
        {
            if (m_molarS.UpToDate)
            {
                return m_molarS.Value / Constants.Rgas;
            }
            else if (m_specificS.UpToDate)
            {
                return m_specificS.Value * this.MolarMass / Constants.Rgas;
            }
            double s = 0.0d;
            double[] sk = SpeciesS.Data;
            double[] xk = SpeciesMoleFractions.Data;
            double[] xlofk = SpeciesMoleFractionsLog.Data;
            for (int isp = 0; isp < Nsp; ++isp)
            {
                s += xk[isp] * (sk[isp] - xlofk[isp]);
            }
            return s - PressureAtmLog;
        }


        void calc_speciesCp(double[] cpk)
        {
            if (m_speciesCv.UpToDate)
            {
                double[] cvk = m_speciesCv.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    cpk[isp] = cvk[isp] + 1d;
                }
                return;
            }
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.Cp(Temperature, cpk);
        }

        void calc_speciesCv(double[] cvk)
        {
            if (m_speciesCp.UpToDate)
            {
                double[] cpk = m_speciesCp.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    cvk[isp] = cpk[isp] - 1d;
                }
                return;
            }
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.Cv(Temperature, cvk);
        }

        void calc_speciesH(double[] hk)
        {
            if (m_speciesU.UpToDate)
            {
                double[] uk = m_speciesU.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    hk[isp] = uk[isp] + 1d;
                }
                return;
            }
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.H(Temperature, hk);
        }

        void calc_speciesU(double[] uk)
        {
            if (m_speciesH.UpToDate)
            {
                double[] hk = m_speciesH.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    uk[isp] = hk[isp] - 1d;
                }
                return;
            }
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.U(m_temperature.Value, uk);
        }

        void calc_speciesS(double[] sk)
        {
            if (m_speciesH.UpToDate && m_speciesG.UpToDate)
            {
                double[] hk = m_speciesH.Array;
                double[] gk = m_speciesG.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    sk[isp] = hk[isp] - gk[isp];
                }
            }
            else if (m_speciesU.UpToDate && m_speciesF.UpToDate)
            {
                double[] uk = m_speciesU.Array;
                double[] fk = m_speciesF.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    sk[isp] = uk[isp] - fk[isp];
                }
            }
            else
            {
                var thermo = Model.IdealGasSpeciesThermoPotentials;
                thermo.S(m_temperature.Value, sk);
            }
        }

        void calc_speciesG(double[] gk)
        {
            if (m_speciesF.UpToDate)
            {
                double[] fk = m_speciesF.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    gk[isp] = fk[isp] + 1d;
                }
            }
            else
            {
                var thermo = Model.IdealGasSpeciesThermoPotentials;
                thermo.G(m_temperature.Value, gk);
            }
        }

        void calc_speciesF(double[] fk)
        {
            if (m_speciesG.UpToDate)
            {
                double[] gk = m_speciesG.Array;
                for (int isp = 0; isp < Nsp; ++isp)
                {
                    fk[isp] = gk[isp] - 1d;
                }
            }
            else
            {
                var thermo = Model.IdealGasSpeciesThermoPotentials;
                thermo.F(m_temperature.Value, fk);
            }
        }



        void calc_speciesMolarCp(double[] cpk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarCp(m_temperature.Value, cpk);
        }

        void calc_speciesMolarCv(double[] cvk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarCv(m_temperature.Value, cvk);
        }

        void calc_speciesMolarH(double[] hk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarH(m_temperature.Value, hk);
        }

        void calc_speciesMolarU(double[] uk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarU(m_temperature.Value, uk);
        }

        void calc_speciesMolarS(double[] sk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarS(m_temperature.Value, sk);
        }

        void calc_speciesMolarG(double[] gk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarG(m_temperature.Value, gk);
        }

        void calc_speciesMolarF(double[] fk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.MolarF(m_temperature.Value, fk);
        }



        void calc_speciesSpecificCp(double[] cpk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificCp(m_temperature.Value, cpk);
        }

        void calc_speciesSpecificCv(double[] cvk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificCv(m_temperature.Value, cvk);
        }

        void calc_speciesSpecificH(double[] hk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificH(m_temperature.Value, hk);
        }

        void calc_speciesSpecificU(double[] uk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificU(m_temperature.Value, uk);
        }

        void calc_speciesSpecificS(double[] sk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificS(m_temperature.Value, sk);
        }

        void calc_speciesSpecificG(double[] gk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificG(m_temperature.Value, gk);
        }

        void calc_speciesSpecificF(double[] fk)
        {
            var thermo = Model.IdealGasSpeciesThermoPotentials;
            thermo.SpecificF(m_temperature.Value, fk);
        }
    }
}
